import { Monitor } from "../index"
import * as rrweb from "rrweb"
import type { eventWithTime, listenerHandler } from "@rrweb/types"
import { RecordData } from "./record-screen-type"
import { Base64 } from "js-base64"
import pako from "pako"

export class RecordScreen {
    monitor: InstanceType<typeof Monitor>
    private events: eventWithTime[] = []
    private readonly MAX_RETENTION_MS = 60 * 1000 // 保留30分钟数据
    private cleanupInterval: NodeJS.Timeout | undefined
    private stop: listenerHandler | null | undefined = undefined
    private duration: number = 5

    constructor(monitor: InstanceType<typeof Monitor>, duration?: number) {
        this.monitor = monitor
        if (duration) {
            this.duration = duration // 录制报错前后几秒的数据
        }
        this.startRecording()
        this.startAutoCleanup()
    }

    // 启动录屏
    startRecording(): void {
        this.stop = rrweb.record({
            emit: (event: eventWithTime, isCheckout?: boolean) => {
                this.events.push({
                    ...event,
                    timestamp: Date.now()
                })
                // 如果是检查点且需要上报
                if (isCheckout && this.monitor.hasError) {
                    this.handleCheckout()
                }
            },
            recordCanvas: true, // 录制canvas内容
            checkoutEveryNms: this.duration * 1000, // 根据duration设置检查点间隔
            blockClass: "no-record", // 忽略特定元素
            maskTextClass: "mask-data" // 忽略敏感文本
        })
    }

    /**
     * 处理检查点
     */
    private handleCheckout() {
        if (this.monitor.hasError && this.monitor.recordScreenId) {
            const { recordScreenId } = this.monitor
            const errorTime = Date.now()

            // 上报录屏数据
            this.uploadRecordSegment(recordScreenId, errorTime, window.location.href).finally(() => {
                this.monitor.resetErrorStatus() // 上报完成后重置状态
            })
        }
    }

    /**
     * 获取最近的事件（根据duration）
     */
    private getRecentEvents(): eventWithTime[] {
        const now = Date.now()
        return this.events.filter(e => e.timestamp >= now - this.duration * 1000 && e.timestamp <= now)
    }

    /**
     * 自动清理过期数据
     */
    private startAutoCleanup(): void {
        this.cleanupInterval = setInterval(
            () => {
                const cutoff = Date.now() - this.MAX_RETENTION_MS
                this.events = this.events.filter(e => e.timestamp >= cutoff)
            },
            5 * 60 * 1000
        ) // 每5分钟检查一次
    }

    /**
     * 获取错误前后的录屏数据
     */
    private getRelevantEvents(errorTime: number): eventWithTime[] {
        const start = errorTime - this.duration * 1000
        const end = errorTime + this.duration * 1000
        return this.events.filter(e => e.timestamp >= start && e.timestamp <= end)
    }

    /**
     * 压缩数据
     */
    private zip(data: any): string {
        try {
            if (!data) return ""
            const str = Base64.encode(typeof data === "object" ? JSON.stringify(data) : String(data))
            return Base64.fromUint8Array(pako.gzip(str))
        } catch (e) {
            console.warn("录屏数据压缩失败，返回原始数据", e)
            return typeof data === "string" ? data : JSON.stringify(data)
        }
    }

    /**
     * 上报录屏片段（供ErrorMonitor调用）
     */
    async uploadRecordSegment(recordScreenId: string, errorTime: number, pageUrl: string): Promise<RecordData> {
        // 等待指定时间，继续记录数据
        await new Promise<void>(resolve => {
            setTimeout(() => {
                resolve()
            }, this.duration * 1000)
        })

        const events = this.getRelevantEvents(errorTime)
        const compressedEvents = this.zip(events) // 调用压缩函数

        try {
            await this.monitor.transport?.sendRecordScreen({
                recordScreenId,
                events: compressedEvents, // 使用压缩后的数据
                time: errorTime,
                pageUrl
            })
        } catch (error) {
            console.error("上传录屏片段失败", error)
        }

        return {
            recordScreenId,
            events: compressedEvents, // 返回压缩后的数据
            time: errorTime,
            pageUrl
        }
    }

    /**
     * 销毁实例
     */
    destroy(): void {
        this.stop?.()
        clearInterval(this.cleanupInterval)
        this.events = []
    }
}
